
package edu.uhcl.csci4838.group3.view;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.app.Activity;
import android.app.ListActivity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.GridView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
import edu.uhcl.csci4838.group3.R;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper.CursorCourseTimeOverride;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper.CursorCoursesForAgendaView;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper.CursorHoliday;
import edu.uhcl.csci4838.group3.date.DateConstants;
import edu.uhcl.csci4838.group3.view.help.HelpActivity;

public class CalendarActivity extends ListActivity {
	
	public Calendar month;
	public CalendarAdapter adapter;
	public Handler handler;
	
	// Db
	private BackpackEduDatabaseHelper dbHelper;
	
	// Cursors
	private CursorCoursesForAgendaView courses;
	private CursorCourseTimeOverride courseTimeOverrides;
	private CursorHoliday holidays;
	
	// Misc.
	private GregorianCalendar gCal;
	private long millisecondsPerday;
	private long rightBound;
	private long todayYear, todayMonth, todayDay;
	private ArrayList<Integer> overrides;
	private ArrayList<ArrayList<Integer>> meetingDays;
	private ArrayList<CourseMeetingEntry> dailyEntries;
	private ArrayList<ArrayList<CourseMeetingEntry>> monthlyEntries;
	private SimpleAdapter taskListAdapter;
	private List<HashMap<String, String>> taskListData;
	private int currentDay;
	
	// An Object to hold the Course meeting entry
	public static class CourseMeetingEntry implements Comparable {
		
		private String courseName;
		private String holidayName;
		private long id;
		private boolean isHoliday;
		private int startTimeHour;
		private int startTimeMinute;
		private int endTimeHour;
		private int endTimeMinute;
		
		public long getId() {
			return this.id;
		}
		
		public void setId(long id) {
			this.id = id;
		}
		
		public boolean getIsHoliday() {
			return this.isHoliday;
		}
		
		public void setIsHoliday(boolean isHoliday) {
			this.isHoliday = isHoliday;
		}
		
		public String getHolidayName() {
			return this.holidayName;
		}
		
		public void setHolidayName(String holidayName) {
			this.holidayName = holidayName;
		}
		
		public String getCourseName() {
			return this.courseName;
		}
		
		public void setCourseName(String courseName) {
			this.courseName = courseName;
		}
		
		public int getStartTimeHour() {
			return this.startTimeHour;
		}
		
		public void setStartTimeHour(int startTimeHour) {
			this.startTimeHour = startTimeHour;
		}
		
		public int getStartTimeMinute() {
			return this.startTimeMinute;
		}
		
		public void setStartTimeMinute(int startTimeMinute) {
			this.startTimeMinute = startTimeMinute;
		}
		
		public int getEndTimeHour() {
			return this.endTimeHour;
		}
		
		public void setEndTimeHour(int endTimeHour) {
			this.endTimeHour = endTimeHour;
		}
		
		public int getEndTimeMinute() {
			return this.endTimeMinute;
		}
		
		public void setEndTimeMinute(int endTimeMinute) {
			this.endTimeMinute = endTimeMinute;
		}
		
		public int compareTo(Object o) {
			int hour = ((CourseMeetingEntry) o).getStartTimeHour();
			int minute = ((CourseMeetingEntry) o).getStartTimeMinute();
			
			if (this.getStartTimeHour() == hour) {
				return this.getStartTimeMinute()
						- minute;
			}
			return this.getStartTimeHour()
					- hour;
			
		}
	}
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
		setContentView(R.layout.layout_calendar);
		
		month = Calendar.getInstance();
		// onNewIntent(getIntent());
		// month.set(Integer.parseInt(dateArr[0]), Integer.parseInt(dateArr[1]),
		// Integer.parseInt(dateArr[2]));
		
		// Initialize the Db
		dbHelper = new BackpackEduDatabaseHelper(this);
		
		// Initialize the time
		gCal = new GregorianCalendar();
		gCal = new GregorianCalendar(gCal.get(Calendar.YEAR),
				gCal.get(Calendar.MONTH),
				1);
		GregorianCalendar tempCal = new GregorianCalendar(gCal.get(Calendar.YEAR),
				gCal.get(Calendar.MONTH),
				gCal.getActualMaximum(Calendar.DAY_OF_MONTH) + 1);
		rightBound = tempCal.getTimeInMillis();
		
		// Initialize the cursors
		courses = dbHelper.getCoursesForAgendaView(gCal.getTimeInMillis(),
				rightBound);
		courseTimeOverrides = dbHelper.getCourseTimeOverridesForAgendaView(gCal.getTimeInMillis(),
				rightBound);
		holidays = dbHelper.getHolidaysForAgendaView(gCal.getTimeInMillis(),
				rightBound);
		
		// Initialize the adapter
		adapter = new CalendarAdapter(this,
				month);
		
		// Misc.
		millisecondsPerday = 24 * 60 * 60 * 1000;
		
		// Reference the xml layouts
		GridView gridview = (GridView) findViewById(R.id.gridview);
		gridview.setAdapter(adapter);
		TextView title = (TextView) findViewById(R.id.title);
		title.setText(android.text.format.DateFormat.format("MMMM yyyy",
				month));
		TextView previous = (TextView) findViewById(R.id.previous);
		taskListData = new ArrayList<HashMap<String, String>>();
		taskListAdapter = new SimpleAdapter(CalendarActivity.this,
				taskListData,
				R.layout.item_calendar_list,
				new String[] { "id", "courseName", "times" },
				new int[] { R.id.tv_id, R.id.tv_courseName, R.id.tv_date });
		setListAdapter(taskListAdapter);
		
		updateCalendar();
		
		previous.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				if (month.get(Calendar.MONTH) == month.getActualMinimum(Calendar.MONTH)) {
					month.set((month.get(Calendar.YEAR) - 1),
							month.getActualMaximum(Calendar.MONTH),
							1);
					
				} else {
					month.set(Calendar.MONTH,
							month.get(Calendar.MONTH) - 1);
				}
				
				gCal = new GregorianCalendar(month.get(Calendar.YEAR),
						month.get(Calendar.MONTH),
						1);
				GregorianCalendar tempCal = new GregorianCalendar(gCal.get(Calendar.YEAR),
						gCal.get(Calendar.MONTH),
						1);
				tempCal.set(Calendar.MONTH,
						gCal.get(Calendar.MONTH) + 1);
				
				rightBound = tempCal.getTimeInMillis();
				
				Log.d("month.get(Calendar.MONTH)",
						""
								+ month.get(Calendar.MONTH));
				Log.d("gCal.get(Calendar.MONTH)",
						""
								+ gCal.get(Calendar.MONTH));
				Log.d("tempCal.get(Calendar.MONTH)",
						""
								+ tempCal.get(Calendar.MONTH));
				
				refreshCalendar();
			}
		});
		
		TextView next = (TextView) findViewById(R.id.next);
		next.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				if (month.get(Calendar.MONTH) == month.getActualMaximum(Calendar.MONTH)) {
					month.set((month.get(Calendar.YEAR) + 1),
							month.getActualMinimum(Calendar.MONTH),
							1);
					
				} else {
					month.set(Calendar.MONTH,
							month.get(Calendar.MONTH) + 1);
					
				}
				
				gCal = new GregorianCalendar(month.get(Calendar.YEAR),
						month.get(Calendar.MONTH),
						1);
				GregorianCalendar tempCal = new GregorianCalendar(gCal.get(Calendar.YEAR),
						gCal.get(Calendar.MONTH),
						1);
				tempCal.set(Calendar.MONTH,
						gCal.get(Calendar.MONTH) + 1);
				
				rightBound = tempCal.getTimeInMillis();
				
				Log.d("month.get(Calendar.MONTH)",
						""
								+ month.get(Calendar.MONTH));
				Log.d("gCal.get(Calendar.MONTH)",
						""
								+ gCal.get(Calendar.MONTH));
				Log.d("tempCal.get(Calendar.MONTH)",
						""
								+ tempCal.get(Calendar.MONTH));
				
				refreshCalendar();
				
			}
		});
		
		gridview.setOnItemClickListener(new OnItemClickListener() {
			public void onItemClick(AdapterView<?> parent,
					View v,
					int position,
					long id) {
				TextView date = (TextView) v.findViewById(R.id.date);
				if (date instanceof TextView
						&& !date.getText()
								.equals("")) {
					String day = date.getText()
							.toString();
					if (day.length() == 1) {
						day = "0"
								+ day;
					}
					
					TextView todayTextView = (TextView) v.findViewById(R.id.courseMeetings);
					
					// Parse string into three string (name, times, id)
					String s = (String) todayTextView.getText();
					String[] parts = s.split("\n");
					
					CalendarActivity.this.taskListData.clear();
					if (parts.length > 1) {
						
						// Loop
						for (int i = 0; i < parts.length; i += 3) {
							HashMap<String, String> hash = new HashMap<String, String>();
							hash.put("id",
									parts[i]);
							hash.put("courseName",
									parts[i + 1]);
							hash.put("times",
									parts[i + 2]);
							CalendarActivity.this.taskListData.add(hash);
						}
					}
					CalendarActivity.this.taskListAdapter.notifyDataSetChanged();
				}
				
			}
		});
	}
	
	/**
	 * XXX
	 * 
	 * @param l
	 * @param v
	 * @param position
	 * @param id
	 */
	/* 
	 * (non-Javadoc)
	 * 
	 * @see android.app.ListActivity#onListItemClick(android.widget.ListView, android.view.View, int, long)
	 */
	@Override
	protected void onListItemClick(ListView l,
			View v,
			int position,
			long id) {
		super.onListItemClick(l,
				v,
				position,
				id);
		TextView tvId = (TextView) v.findViewById(R.id.tv_id);
		Long courseId = Long.valueOf(tvId.getText()
				.toString());
		
		// Start course activity
		final Intent intent = new Intent(CalendarActivity.this.getApplicationContext(),
				CourseActivity.class);
		intent.putExtra(getString(R.string.intent_extra_courseId),
				courseId);
		CalendarActivity.this.startActivity(intent);
	}
	
	public void updateCalendar() {
		
		boolean isOverride = false, isHoliday = false;
		CourseMeetingEntry entry;
		monthlyEntries = new ArrayList<ArrayList<CourseMeetingEntry>>();
		
		Log.d("gCal.getActualMaximum(Calendar.DAY_OF_MONTH)",
				""
						+ gCal.getActualMaximum(Calendar.DAY_OF_MONTH));
		
		unsetCourseTimeOverrides();
		setMeetingDays();
		
		for (int i = 0; i < gCal.getActualMaximum(Calendar.DAY_OF_MONTH); i++) {
			dailyEntries = new ArrayList<CalendarActivity.CourseMeetingEntry>();
			monthlyEntries.add(i,
					dailyEntries);
		}
		
		for (long nextDay = gCal.getTimeInMillis()
				+ millisecondsPerday; gCal.getTimeInMillis() < rightBound; nextDay += millisecondsPerday, gCal.set(Calendar.DAY_OF_MONTH,
				gCal.get(Calendar.DAY_OF_MONTH) + 1)) {
			
			// initialize the daily entries
			dailyEntries = new ArrayList<CalendarActivity.CourseMeetingEntry>();
			
			for (int i = 0; i < courses.getCount(); i++) {
				
				// Move the cursor to the correct position
				courses.moveToPosition(i);
				
				// check to see if today is inside the course meeting period
				if (gCal.getTimeInMillis() < courses.getColStartDate()
						|| gCal.getTimeInMillis() > courses.getColEndDate()) {
					continue;
				}
				
				// Check to see if the course meets today
				if (meetingDays.get(i)
						.contains(gCal.get(Calendar.DAY_OF_WEEK))) {
					
					// Unset the isHoliday and isOverride
					isHoliday = isOverride = false;
					
					// Initialize the entry
					entry = new CourseMeetingEntry();
					
					// Get all the overrides for today
					if (overrides.contains(0)) {
						for (int k = 0; k < courseTimeOverrides.getCount(); k++) {
							
							// Move the cursor to the right position
							courseTimeOverrides.moveToPosition(k);
							
							// Add an entry if there is an override
							if (courses.getColId() == courseTimeOverrides.getColCourseId()
									&& overrides.get(k)
											.intValue() == 0
									&& courseTimeOverrides.getColDate() >= gCal.getTimeInMillis()
									&& courseTimeOverrides.getColDate() < nextDay) {
								entry = new CourseMeetingEntry();
								entry.setIsHoliday(false);
								entry.setId(courseTimeOverrides.getColCourseId());
								entry.setCourseName(courseTimeOverrides.getColCourseName());
								entry.setStartTimeHour(courseTimeOverrides.getColStartTimeHour());
								entry.setStartTimeMinute(courseTimeOverrides.getColStartTimeMinute());
								entry.setEndTimeHour(courseTimeOverrides.getColEndTimeHour());
								entry.setEndTimeMinute(courseTimeOverrides.getColEndTimeMinute());
								dailyEntries.add(entry);
								
								// Set isOverride to true
								isOverride = true;
								
								// Set the override flag to taken
								overrides.set(k,
										1);
								
							}
						}
					} // end getting all overrides for today
					
					if (!(isOverride)) {
						// Check to see if today is within the holiday
						// period defined for the semester the course
						// belongs to
						for (int k = 0; k < holidays.getCount(); k++) {
							
							// Move the cursor to the correct
							// position
							holidays.moveToPosition(k);
							
							if (courses.getColSemesterId() == holidays.getColSemesterId()
									&& gCal.getTimeInMillis() >= holidays.getColStartDate()
									&& gCal.getTimeInMillis() <= holidays.getColEndDate()) {
								
								// Set the isHoliday flag to true
								isHoliday = true;
								
								// Add a new entry for meeting that is
								// canceled due to holiday
								entry = new CourseMeetingEntry();
								entry.setIsHoliday(true);
								entry.setId(courses.getColId());
								entry.setCourseName(courses.getColName());
								entry.setHolidayName(holidays.getColName());
								entry.setId(courses.getColId());
								entry.setStartTimeHour(-1);
								entry.setStartTimeMinute(-1);
								entry.setEndTimeHour(-1);
								entry.setEndTimeMinute(-1);
								dailyEntries.add(entry);
								
								Log.d("Holiday",
										entry.getCourseName());
								Log.d("Holiday",
										entry.getHolidayName());
								
								break;
							}
						}
					} // end checking holiday
					
					if (!(isOverride || isHoliday)) {
						
						// Initializing the entry
						entry.setCourseName(courses.getColName());
						entry.setIsHoliday(false);
						entry.setId(courses.getColId());
						entry.setStartTimeHour(courses.getColStartTimeHour());
						entry.setStartTimeMinute(courses.getColStartTimeMinute());
						entry.setEndTimeHour(courses.getColEndTimeHour());
						entry.setEndTimeMinute(courses.getColEndTimeMinute());
						
						// Add the entry to the batch
						dailyEntries.add(entry);
					}
					
				} // end Check to see if the course meets today
				
			} // end Check to see if today is in the course meeting
				// period
			
			// Check to see if there are any missing overrides
			if (overrides.contains(0)) {
				for (int j = 0; j < courseTimeOverrides.getCount(); j++) {
					
					// Move the cursor to the correct position
					courseTimeOverrides.moveToPosition(j);
					
					// Get all overrides for today
					if (overrides.get(j) == 0
							&& courseTimeOverrides.getColDate() >= gCal.getTimeInMillis()
							&& courseTimeOverrides.getColDate() < nextDay) {
						
						// Initialize the entry
						entry = new CourseMeetingEntry();
						
						// Initializing the entry
						entry.setCourseName(courseTimeOverrides.getColCourseName());
						entry.setIsHoliday(false);
						entry.setId(courseTimeOverrides.getColCourseId());
						entry.setStartTimeHour(courseTimeOverrides.getColStartTimeHour());
						entry.setStartTimeMinute(courseTimeOverrides.getColStartTimeMinute());
						entry.setEndTimeHour(courseTimeOverrides.getColEndTimeHour());
						entry.setEndTimeMinute(courseTimeOverrides.getColEndTimeMinute());
						
						// Add the entry to the batch
						dailyEntries.add(entry);
						
						// Unset the position in the override
						// array list
						overrides.add(j,
								1);
					}
				}
			} // end getting all missing overrides for today
				// end getting all the entries for today
			
			// check to see if no entries and today is a holiday
			if (dailyEntries.size() == 0
					&& holidays.getCount() > 0) {
				for (int i = 0; i < holidays.getCount(); i++) {
					
					// Move the cursor to the correct position
					holidays.moveToPosition(i);
					
					// Get at most one holiday if any
					if (gCal.getTimeInMillis() >= holidays.getColStartDate()
							&& gCal.getTimeInMillis() <= holidays.getColEndDate()) {
						entry = new CourseMeetingEntry();
						entry.setIsHoliday(true);
						entry.setId(-1);
						dailyEntries.add(entry);
						break;
					}
					
				}
				
			}
			
			// sort the entries for today if needed
			if (dailyEntries.size() > 1) {
				Collections.sort(dailyEntries);
			}
			
			// add the daily entries to the monthly entries
			monthlyEntries.set(gCal.get(Calendar.DAY_OF_MONTH) - 1,
					dailyEntries);
			
		} // end getting all entries for this month
		
		// set the monthly entries
		adapter.setMonthlyEntries(monthlyEntries);
		adapter.notifyDataSetChanged();
		
	}
	
	/**
	 * XXX
	 */
	/* 
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onDestroy()
	 */
	@Override
	protected void onDestroy() {
		courses.close();
		holidays.close();
		courseTimeOverrides.close();
		dbHelper.close();
		super.onDestroy();
	}
	
	// unsetCourseTimeOverrides()
	private void unsetCourseTimeOverrides() {
		overrides = new ArrayList<Integer>();
		for (int i = 0; i < courseTimeOverrides.getCount(); i++) {
			overrides.add(0);
		}
	}
	
	// getMeetingDays()
	private void setMeetingDays() {
		
		// Vars
		meetingDays = new ArrayList<ArrayList<Integer>>();
		ArrayList<Integer> tempArlInt;
		int dayBitField;
		
		// Go through all the courses in the cursor
		for (int i = 0; i < courses.getCount(); i++) {
			
			// Move cursor to correct position
			courses.moveToPosition(i);
			
			// Add the meeting day to the array list
			tempArlInt = new ArrayList<Integer>();
			dayBitField = courses.getColDayBitField();
			if ((dayBitField & DateConstants.SUNDAY_BIT) == DateConstants.SUNDAY_BIT) {
				tempArlInt.add(1);
			}
			if ((dayBitField & DateConstants.MONDAY_BIT) == DateConstants.MONDAY_BIT) {
				tempArlInt.add(2);
			}
			if ((dayBitField & DateConstants.TUESDAY_BIT) == DateConstants.TUESDAY_BIT) {
				tempArlInt.add(3);
			}
			if ((dayBitField & DateConstants.WEDNESDAY_BIT) == DateConstants.WEDNESDAY_BIT) {
				tempArlInt.add(4);
			}
			if ((dayBitField & DateConstants.THURSDAY_BIT) == DateConstants.THURSDAY_BIT) {
				tempArlInt.add(5);
			}
			if ((dayBitField & DateConstants.FRIDAY_BIT) == DateConstants.FRIDAY_BIT) {
				tempArlInt.add(6);
			}
			if ((dayBitField & DateConstants.SATURDAY_BIT) == DateConstants.SATURDAY_BIT) {
				tempArlInt.add(7);
			}
			
			// Adding the array list to the container
			meetingDays.add(tempArlInt);
		}
	}
	
	/**
	 * XXX
	 * 
	 * @param menu
	 *            XXX
	 * @return XXX
	 */
	/* 
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
	 */
	@Override
	public boolean onCreateOptionsMenu(final Menu menu) {
		this.getMenuInflater()
				.inflate(R.menu.options_calendar,
						menu);
		return true;
	}
	
	/**
	 * XXX
	 * 
	 * @param item
	 *            XXX
	 * @return XXX
	 */
	/* 
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
	 */
	@Override
	public boolean onOptionsItemSelected(final MenuItem item) {
		switch (item.getItemId()) {
			case R.id.menu_item_settings:
				this.startActivity(new Intent(this.getApplicationContext(),
						SettingsActivity.class));
				return true;
			case R.id.menu_item_about:
				this.startActivity(new Intent(this.getApplicationContext(),
						AboutActivity.class));
				return true;
			case R.id.menu_item_help:
				this.startActivity(new Intent(this.getApplicationContext(),
						HelpActivity.class));
				return true;
			default:
				Log.wtf("BackpackEDU",
						"Undefined options menu item.");
				return super.onOptionsItemSelected(item);
		}
	}
	
	public void refreshCalendar() {
		
		// close current cursors
		courses.close();
		holidays.close();
		courseTimeOverrides.close();
		
		// Initialize the cursors
		courses = dbHelper.getCoursesForAgendaView(gCal.getTimeInMillis(),
				rightBound);
		courseTimeOverrides = dbHelper.getCourseTimeOverridesForAgendaView(gCal.getTimeInMillis(),
				rightBound);
		holidays = dbHelper.getHolidaysForAgendaView(gCal.getTimeInMillis(),
				rightBound);
		
		TextView title = (TextView) findViewById(R.id.title);
		
		adapter.refreshDays();
		updateCalendar(); // generate some random calendar items
		
		title.setText(android.text.format.DateFormat.format("MMMM yyyy",
				month));
	}
	
	public void onNewIntent(Intent intent) {
		String date = intent.getStringExtra("date");
		String[] dateArr = date.split("-"); // date format is yyyy-mm-dd
		month.set(Integer.parseInt(dateArr[0]),
				Integer.parseInt(dateArr[1]),
				Integer.parseInt(dateArr[2]));
	}
}
